(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    typeof define === 'function' && define.amd ? define(factory) :
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.pageby = factory());
})(this, (function () { 'use strict';

    // 等待DOM加载完毕执行
    function onReady (callback) {
        var readyState = document.readyState;
        if (readyState === 'interactive' || readyState === 'complete') {
            callback();
        } else {
            window.addEventListener("DOMContentLoaded", callback);
        }
    }

    var normalize = "/* 统一不同浏览器的基础样式 *//* 防止iPhone在坚屏转向横屏时放大文字 */\n html{\n\n-ms-text-size-adjust: 100%;\n\n-webkit-text-size-adjust: 100%;\n/* 统一行高 */\nline-height: 1.15;\n\n}\n/* 兼容部分手机下border不显示问题 */\n button, input{\n\nborder: 1px solid #b2b2bd;\n\n}\n/* 修正旧浏览器未定义的块级元素 */\n article, footer, header, nav, section{\n\ndisplay: block;\n\n}\n/* 修正旧浏览器未定义的行内块元素 */\n canvas, svg{\n\ndisplay: inline-block;\n\n}\n/* 统一不同浏览器盒子尺寸计算方法 */\n *{\n\nbox-sizing: border-box;\n\n}\n/* 去掉IE浏览器输入框叉叉和眼睛 */\n ::-ms-clear, ::-ms-reveal{\n\ndisplay: none;\n\n}\n/* 针对火狐浏览器在img标签没有src时候的差异修复 */\n img{\n\ndisplay: inline-block;\n\n}\n/* 设置默认字体为统一的安全字体 */\n html{\n\nfont-family: sans-serif;\n\n}\n/* 默认去掉下划线 */\n a{\n\ntext-decoration: none;\n\n}\n/* 去掉前置索引 */\n li{\n\nlist-style-type: none;\n\n}\n/* 去掉不喜欢的间距 */\n ul, ol, li, p, h1, h2, h3, h4, h5, h6{\n\n-webkit-margin-before: 0;\n\n-webkit-margin-after: 0;\n\n-webkit-padding-start: 0;\n/* 去掉不喜欢的间距，针对火狐浏览器等 */\nmargin-block-end: 0;\n\nmargin-block-start: 0;\n\npadding-inline-start: 0;\n/* 修改IE和其它浏览器不一致问题 */\npadding: 0;\n\nmargin: 0;\n\n}\n/* 去掉默认的8px */\n body{\n\nmargin: 0;\n\n}\n/* 设置默认表格边框合并为一个单一的边框 */\n table{\n\nborder-collapse: collapse;\n\n}\n";

    var style = "\n/* // 代码着色器\r */\n\n .pageby-pre{\n\noutline: 1px solid #d3d3d4;\n\n}\n\n .pageby-pre:hover{\n\noutline-color: black;\n\n}\n\n/* // 复制按钮\r */\n\n .pageby-pre>.copy-icon{\n\noutline: none;\n\n}\n\n .pageby-pre>.copy-icon[copied]{\n\noutline: 1px solid #343848;\n\nborder-radius: 0 4px 4px 0;\n\n}\n\n .pageby-pre>.copy-icon[copied]:before{\n\nposition: absolute;\n\nleft: -64px;\n\ntop: -1px;\n\ndisplay: block;\n\nborder-radius: 4px 0 0 4px;\n\nwidth: 64px;\n\ntext-align: center;\n\nfont-size: 12px;\n\nfont-weight: 500;\n\ncolor: #ffffff;\n\nbackground-color: #343848;\n\nwhite-space: nowrap;\n\ncontent: attr(data-before);\n\nheight: 32px;\n\nline-height: 32px;\n\n}\n\n .pageby-pre>.copy-icon[copied='no']{\n\noutline: 1px solid #ff0000;\n\n}\n\n .pageby-pre>.copy-icon[copied='no']:before{\n\nbackground-color: #ff0000;\n\n}\n";

    onReady(function () {
        var styleEl = document.createElement("style");
        styleEl.innerHTML = normalize + "\n" + style;

        var headEl = document.getElementsByTagName("head")[0];
        headEl.insertBefore(styleEl, headEl.childNodes[0]);
    });

    var colors$2 = {
        "annotation": "#6a9955",/*注释颜色*/
        "insign": "#9e9e9e",/*符号颜色*/
        "selector": "#1e50b3",/*选择器*/
        "attrKey": "#1e83b1",/*属性名称颜色*/
        "attrValue": "#ac4c1e"/*属性值颜色*/
    };

    function cssShader (textString) {
        var shaderArray = [];

        // 当前面对的
        var i = 0;

        // 获取往后n个值
        var nextNValue = function (n) {
            return textString.substring(i, n + i > textString.length ? textString.length : n + i);
        };

        var template = "";

        // 1:选择器 tag
        // 2:属性名 attr
        // 3:属性值 string
        var state = "tag";

        // 初始化模板，开始文本捕获
        var initTemplate = function () {
            if (template != "") {
                shaderArray.push({
                    color: {
                        tag: colors$2.selector,
                        attr: colors$2.attrKey,
                        string: colors$2.attrValue
                    }[state],
                    content: template
                });
            }

            template = "";
        };

        while (true) {

            /* 1.注释 */

            if (nextNValue(2) == '/*') {

                initTemplate();
                while (nextNValue(2) !== '*/' && i < textString.length) {
                    template += textString[i++];
                }

                shaderArray.push({
                    color: colors$2.annotation,
                    content: template + nextNValue(2)
                });
                i += 2;
                template = "";

            }

            /* 2.字符串 */

            else if (["'", '"'].indexOf(nextNValue(1)) > -1) {

                var strBorder = nextNValue(1);
                initTemplate();

                do {
                    template += textString[i++];
                } while (nextNValue(1) != strBorder && i < textString.length)

                // 因为可能是没有字符导致的结束
                if (nextNValue(1) != strBorder) {
                    strBorder = "";
                } else {
                    i += 1;
                }

                shaderArray.push({
                    color: colors$2.attrValue,
                    content: template + strBorder
                });
                template = "";

            }

            /* 3.边界 */

            else if ([":", '{', '}', ";"].indexOf(nextNValue(1)) > -1) {

                initTemplate();
                shaderArray.push({
                    color: colors$2.insign,
                    content: nextNValue(1)
                });
                template = "";

                if (nextNValue(1) == '{' || nextNValue(1) == ';') {
                    state = 'attr';
                } else if (nextNValue(1) == '}') {
                    state = 'tag';
                } else {
                    state = 'string';
                }

                i += 1;
            }

            /* 追加字符 */

            else {
                if (i >= textString.length) {
                    initTemplate();
                    break;
                } else {
                    template += textString[i++];
                }
            }

        }
        return shaderArray;
    }

    var colors$1 = {
        "text": "#000000",/*文本颜色*/
        "annotation": "#6a9955",/*注释颜色*/
        "insign": "#9e9e9e",/*符号颜色*/
        "key": "#ff0000",/*关键字颜色*/
        "string": "#ac4c1e",/*字符串颜色*/
        "funName": "#1e50b3",/*函数名称颜色*/
        "execName": "#1e83b1"/*执行方法颜色*/
    };

    // JS关键字
    var keyWords = [
        "abstract", "arguments", "boolean", "break", "byte",
        "case", "catch", "char", "class", "const",
        "continue", "debugger", "default", "delete", "do",
        "double", "else", "enum", "eval", "export",
        "extends", "false", "final", "finally", "float",
        "for", "function", "goto", "if", "implements",
        "import", "in", "instanceof", "int", "interface",
        "let", "long", "native", "new", "null",
        "package", "private", "protected", "public", "return",
        "short", "static", "super", "switch", "synchronized",
        "this", "throw", "throws", "transient", "true",
        "try", "typeof", "var", "void", "volatile",
        "while", "with", "yield"
    ];

    function javascriptShader (textString) {
        var shaderArray = [];

        // 当前面对的
        var i = 0;

        // 获取往后n个值
        var nextNValue = function (n) {
            return textString.substring(i, n + i > textString.length ? textString.length : n + i);
        };

        var template = "";

        // 初始化模板，开始文本捕获
        var initTemplate = function () {
            if (template != "") {

                // 考虑开始的(
                if (template[0] == '(') {
                    shaderArray.push({
                        color: colors$1.insign,
                        content: "("
                    });
                    template = template.substring(1);
                }

                shaderArray.push({
                    color: colors$1.text,
                    content: template
                });
            }

            template = "";
        };

        while (true) {

            /* 1.注释1 */

            if (nextNValue(2) == '/*') {

                initTemplate();
                while (nextNValue(2) !== '*/' && i < textString.length) {
                    template += textString[i++];
                }

                shaderArray.push({
                    color: colors$1.annotation,
                    content: template + nextNValue(2)
                });
                i += 2;
                template = "";

            }

            /* 2.注释2 */

            else if (nextNValue(2) == '//') {
                initTemplate();
                while (nextNValue(1) !== '\n' && i < textString.length) {
                    template += textString[i++];
                }
                shaderArray.push({
                    color: colors$1.annotation,
                    content: template
                });
                template = "";
            }

            /* 3.字符串 */

            else if (["'", '"', '`'].indexOf(nextNValue(1)) > -1) {

                var strBorder = nextNValue(1);
                initTemplate();

                do {
                    template += textString[i++];
                } while (nextNValue(1) != strBorder && i < textString.length)

                // 因为可能是没有字符导致的结束
                if (nextNValue(1) != strBorder) {
                    strBorder = "";
                } else {
                    i += 1;
                }

                shaderArray.push({
                    color: colors$1.string,
                    content: template + strBorder
                });
                template = "";

            }


            /* 4.函数定义 */

            else if (nextNValue(1) == '(' && (template[0] == ' ' || (i - template.length - 1 >= 0 && textString[i - template.length - 1] == " "))) {
                shaderArray.push({
                    color: colors$1.funName,
                    content: template
                });
                i += 1;
                template = "(";

            }

            /* 5.方法调用 */

            else if (nextNValue(1) == '(') {

                shaderArray.push({
                    color: colors$1.execName,
                    content: template
                });
                i += 1;
                template = "(";
            }

            /* 6.边界 */

            else if ([";", '{', '}', '(', ')', '.', '\n', '=', '+', '>', '<', '[', ']', '-', '*', '/', '^', '*', '!'].indexOf(nextNValue(1)) > -1) {

                initTemplate();
                shaderArray.push({
                    color: colors$1.insign,
                    content: nextNValue(1)
                });
                template = "";
                i += 1;
            }

            /* 7.关键字 */

            else if (nextNValue(1) == ' ' && keyWords.indexOf(template.trim()) > -1) {

                shaderArray.push({
                    color: colors$1.key,
                    content: template + " "
                });
                template = "";
                i += 1;

            }

            /* 追加字符 */

            else {
                if (i >= textString.length) {
                    initTemplate();
                    break;
                } else {
                    template += textString[i++];
                }
            }

        }

        return shaderArray;
    }

    var colors = {
        "text": "#000000",/*文本颜色*/
        "annotation": "#6a9955",/*注释颜色*/
        "insign": "#9e9e9e",/*符号颜色*/
        "node": "#1e50b3",/*结点颜色*/
        "attrKey": "#1e83b1",/*属性名称颜色*/
        "attrValue": "#ac4c1e"/*属性值颜色*/
    };

    function htmlShader (textString) {
        textString = textString.replace(/&lt;/g, "<").replace(/&gt;/g, ">");
        var shaderArray = [];

        // 当前面对的
        var i = 0;

        // 获取往后n个值
        var nextNValue = function (n) {
            return textString.substring(i, n + i > textString.length ? textString.length : n + i);
        };

        var template = "";

        // 初始化模板，开始文本捕获
        var initTemplate = function () {
            if (template != "") {
                shaderArray.push({
                    color: colors.text,
                    content: template
                });
            }

            template = "";
        };

        // 匹配属性值模板
        var getAttrValueTemplate = function () {
            var endStr = " ";
            // 寻找属性值边界
            if (nextNValue(1) == '"') endStr = '"';
            if (nextNValue(1) == "'") endStr = "'";

            // 到达边界前一直寻找下一个
            do {
                template += textString[i++];
            } while (nextNValue(1) != endStr && i < textString.length);

            // 如果是匹配成功而不是匹配到末尾
            if (endStr != " " && i < textString.length) {
                template += endStr;
                i += 1;
            }

            shaderArray.push({
                color: colors.attrValue,
                content: template
            });
            template = "";
        };

        while (true) {

            /* 1.注释 */

            if (nextNValue(4) == '<!--') {

                initTemplate();
                while (nextNValue(3) !== '-->' && i < textString.length) {
                    template += textString[i++];
                }

                shaderArray.push({
                    color: colors.annotation,
                    content: template + nextNValue(3)
                });
                i += 3;
                template = "";

            }

            /* 2.</ */

            else if (nextNValue(2) == '</') {

                initTemplate();
                shaderArray.push({
                    color: colors.insign,
                    content: "</"
                });
                i += 2;

                while (nextNValue(1) !== '>' && i < textString.length) {
                    template += textString[i++];
                }

                if (template != "") {
                    shaderArray.push({
                        color: colors.node,
                        content: template
                    });
                    template = "";

                    if (i < textString.length) {
                        shaderArray.push({
                            color: colors.insign,
                            content: ">"
                        });
                        i += 1;
                    }

                }
            }

            /* 3.< */

            else if (nextNValue(1) == '<' && nextNValue(2) != '< ') {

                var specialTag = "";

                initTemplate();
                shaderArray.push({
                    color: colors.insign,
                    content: "<"
                });
                i += 1;

                // 寻找标签名称
                while (nextNValue(1) != '>' && nextNValue(1) != ' ' && i < textString.length) {
                    template += textString[i++];
                }
                if (template != '') {

                    // 针对style和script这样特殊的标签，内部需要调用对应的着色器着色
                    if (template == "style" || template == 'script') {
                        specialTag = "</" + template + ">";
                    }

                    shaderArray.push({
                        color: colors.node,
                        content: template
                    });

                    template = '';
                    if (i < textString.length) {

                        // 寻找标签属性
                        while (i < textString.length) {

                            // 遇到这个表示标签结束了
                            // 也就意味着标签匹配结束
                            if (nextNValue(1) == ">") {

                                initTemplate();
                                shaderArray.push({
                                    color: colors.insign,
                                    content: ">"
                                });
                                i += 1;
                                break;
                            }

                            // 如果是空格，表示是属性之间，接着查看下一个即可
                            else if (nextNValue(1) != ' ') {

                                initTemplate();

                                // 匹配属性名称
                                if (nextNValue(1) != '"' && nextNValue(1) != "'") {

                                    // 如果不是=或>和空格就继续
                                    while (nextNValue(1) != "=" && nextNValue(1) != '>' && i < textString.length && nextNValue(1) != " ") {
                                        template += textString[i++];
                                    }
                                    if (template != "") {
                                        shaderArray.push({
                                            color: colors.attrKey,
                                            content: template
                                        });
                                        template = "";

                                        // 如果下一个是=，就接着找属性值
                                        if (nextNValue(1) == '=') {
                                            shaderArray.push({
                                                color: colors.insign,
                                                content: "="
                                            });
                                            i += 1;


                                            if (i < textString.length && nextNValue(1) != " " && nextNValue(1) != '>') {
                                                // 寻找属性值
                                                getAttrValueTemplate();

                                            }
                                        }
                                    } else {
                                        template += textString[i++];
                                    }
                                } else if (nextNValue(1) == '=') {
                                    shaderArray.push({
                                        color: colors.insign,
                                        content: "="
                                    });
                                    i += 1;
                                } else {
                                    if (i < textString.length && nextNValue(1) != " " && nextNValue(1) != '>') {

                                        getAttrValueTemplate();

                                    }
                                }

                            } else {
                                template += textString[i++];
                            }

                        }

                    }

                }

                if (specialTag != "") {

                    var oldI = i, oldTemplate = template, langHelp, innerShaderArray;
                    while (nextNValue(specialTag.length) != specialTag && i < textString.length) {
                        template += textString[i++];
                    }

                    if (i < textString.length) {

                        langHelp = specialTag.replace(/<\//, '');

                        innerShaderArray = {
                            "style>": cssShader,
                            "script>": javascriptShader
                        }[langHelp](template);

                        innerShaderArray.forEach(function (innerShader) {
                            shaderArray.push(innerShader);
                        });

                        template = "";
                    } else {
                        template = oldTemplate;
                        i = oldI;
                    }

                }

            }

            /* 追加字符 */

            else {
                if (i >= textString.length) {
                    initTemplate();
                    break;
                } else {
                    template += textString[i++];
                }
            }

        }

        for (i = 0; i < shaderArray.length; i++) {
            shaderArray[i].content = shaderArray[i].content.replace(/</g, "&lt;").replace(/>/g, "&gt;");
        }

        return shaderArray;
    }

    var copyImage = "";

    // 修改样式
    function setStyle (el, styles) {
        for (var key in styles) {
            el.style[key] = styles[key];
        }
    }

    var copyString = function (source, prompt) {
        var textareaEl = document.createElement('textarea');
        textareaEl.innerHTML = source;

        document.body.appendChild(textareaEl);
        textareaEl.select();

        try {
            var result = window.document.execCommand("copy", false, null);

            if (result) {
                prompt(true);
            } else {
                prompt(false);
            }
        } catch (e) {
            prompt(false);
            console.error(e);
        }

        document.body.removeChild(textareaEl);
    };

    // 对特殊转义符号等进行校对
    var replaceCode = function (source) {
        return source
            .replace(/\&amp;/g, '&')
            .replace(/\&lt;/g, '<')
            .replace(/\&gt;/g, '>');
    };

    function doShader (el) {
        var preEls = el.getElementsByTagName('pre');
        for (var i = preEls.length - 1; i > -1; i--) {
            (function (i) {
                var shaderJSON = null;
                var source = replaceCode(preEls[i].innerHTML.trim());

                switch (preEls[i].getAttribute('tag')) {
                    case "html": {
                        shaderJSON = htmlShader(source);
                        break
                    }
                    case "css": {
                        shaderJSON = cssShader(source);
                        break
                    }
                    case "javascript": {
                        shaderJSON = javascriptShader(source);
                        break
                    }
                    default: {
                        shaderJSON = [{
                            color: "black",
                            content: source
                        }];
                    }
                }

                if (shaderJSON) {
                    var targetEl = document.createElement("div");
                    preEls[i].parentNode.replaceChild(targetEl, preEls[i]);

                    setStyle(targetEl, {
                        fontSize: "13px",
                        fontFamily: "monospace",
                        fontWeight: "400",
                        lineHeight: "20px",
                        position: "relative",
                        overflow: "hidden"
                    });

                    targetEl.setAttribute("class", "pageby-pre");

                    var preEl = document.createElement("pre");
                    targetEl.appendChild(preEl);

                    for (var j = 0; j < shaderJSON.length; j++) {
                        var itemEl = document.createElement('span');
                        preEl.appendChild(itemEl);

                        itemEl.style.color = shaderJSON[j].color;
                        itemEl.innerText = replaceCode(shaderJSON[j].content);
                    }

                    setStyle(preEl, {
                        margin: "0",
                        padding: "10px",
                        paddingRight:"40px",
                        overflow: "auto",
                        width: "100%",
                    });

                    var copyEl = document.createElement('button');
                    targetEl.appendChild(copyEl);

                    copyEl.setAttribute('class', 'copy-icon');

                    setStyle(copyEl, {
                        position: "absolute",
                        right: "5px",
                        top: "5px",
                        width: "30px",
                        height: "30px",
                        cursor: "pointer",
                        backgroundImage: "url(" + copyImage + ")",
                        backgroundPosition: "center",
                        backgroundRepeat: "no-repeat",
                        backgroundSize: "100% auto",
                        backgroundColor: "transparent",
                        border: "none",
                    });

                    copyEl.addEventListener("click", function () {
                        var filterEl = document.createElement('div');
                        filterEl.innerHTML = preEl.innerHTML.replace(/\<br\>/g, '\n');

                        copyString(filterEl.innerText, function (isOk) {
                            copyEl.setAttribute("copied", isOk ? "yes" : "no");
                            copyEl.setAttribute('data-before', isOk ? "复制成功" : "复制失败");
                            setTimeout(function () {
                                copyEl.removeAttribute("copied");
                                copyEl.removeAttribute('data-before');
                                copyEl.blur();
                            }, 700);
                        });

                    });

                }
            })(i);
        }

    }

    function doUrl () {
        var temp = (window.location.hash + "#").split("#")[1].replace(/^\//, '').replace(/\/$/, '').split("?");

        var routerTemp = temp[0].split('/');
        var paramTemp = (window.location.search.replace(/^\?/, "") + (temp[1] ? ("&" + temp[1]) : "")).replace(/^\&/, "");

        var paramResult, paramArray;
        if (paramTemp == "") {
            paramResult = {};
        } else {
            paramArray = paramTemp.split("&"), paramResult = {};
            paramArray.forEach(function (item) {
                var temp = item.split("=");
                paramResult[temp[0]] = decodeURIComponent(temp[1]);
            });
        }

        var resultData = {
            router: routerTemp[0] == '' ? [] : routerTemp,
            params: paramResult,
            origin: window.location.origin
        };

        return resultData;
    }

    //当前正在运动的动画的tick函数堆栈
    var $timers = [];
    //唯一定时器的定时间隔
    var $interval = 13;
    //定时器ID
    var $timerId;

    /**
     * 动画轮播
     * @param {function} doback 轮询函数，有一个形参deep，0-1，表示执行进度
     * @param {number} duration 动画时长，可选
     * @param {function} callback 动画结束回调，可选，有一个形参deep，0-1，表示执行进度
     *
     * @returns {function} 返回一个函数，调用该函数，可以提前结束动画
     */
    function animation(doback, duration, callback) {
        if (arguments.length < 2) duration = 400;
        if (arguments.length < 3) callback = function () { };

        var clock = {
            //把tick函数推入堆栈
            "timer": function (tick, duration, callback) {
                if (!tick) {
                    throw new Error('Tick is required!');
                }
                var id = new Date().valueOf() + "_" + (Math.random() * 1000).toFixed(0);
                $timers.push({
                    "id": id,
                    "createTime": new Date(),
                    "tick": tick,
                    "duration": duration,
                    "callback": callback
                });
                clock.start();
                return id;
            },

            //开启唯一的定时器timerId
            "start": function () {
                if (!$timerId) {
                    $timerId = setInterval(clock.tick, $interval);
                }
            },

            //被定时器调用，遍历timers堆栈
            "tick": function () {
                var createTime, flag, tick, callback, timer, duration, passTime, timers = $timers;

                $timers = [];
                $timers.length = 0;

                for (flag = 0; flag < timers.length; flag++) {
                    //初始化数据
                    timer = timers[flag];
                    createTime = timer.createTime;
                    tick = timer.tick;
                    duration = timer.duration;
                    callback = timer.callback;

                    //执行
                    passTime = (+new Date().valueOf() - createTime.valueOf()) / duration;
                    passTime = passTime > 1 ? 1 : passTime;
                    tick(passTime);
                    if (passTime < 1 && timer.id) {
                        //动画没有结束再添加
                        $timers.push(timer);
                    } else {
                        callback(passTime);
                    }
                }
                if ($timers.length <= 0) {
                    clock.stop();
                }
            },

            //停止定时器，重置timerId=null
            "stop": function () {
                if ($timerId) {
                    clearInterval($timerId);
                    $timerId = null;
                }
            }
        };

        var id = clock.timer(function (deep) {
            //其中deep为0-1，表示改变的程度
            doback(deep);
        }, duration, callback);

        // 返回一个函数
        // 用于在动画结束前结束动画
        return function () {
            var i;
            for (i in $timers) {
                if ($timers[i].id == id) {
                    $timers[i].id = void 0;
                    return;
                }
            }
        };

    }

    var index = {
        shader: doShader,
        url: doUrl,
        animation: animation
    };

    return index;

}));
